package com.example.yckjbigdataservice.properties;

/*import com.huawei.fusioninsight.elasticsearch.transport.common.Configuration;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.elasticsearch.common.transport.TransportAddress;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.URL;
import java.net.URLDecoder;
import java.net.UnknownHostException;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;*/

import com.example.yckjbigdataservice.utils.ESLoginUtil;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import org.apache.http.Header;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicHeader;
import org.apache.http.ssl.SSLContextBuilder;
import org.elasticsearch.client.Node;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;


public class ElasticsearchProperties{

    private static final Logger LOG = LogManager.getLogger(ElasticsearchProperties.class);

    private static final String ELASTICSEARCH_SERVERREALM_PATH = "/elasticsearch/serverrealm";
    private static final String DOMAIN_NAME_SEPARATOR = "@";
    private static final HostnameVerifier HOSTNAME_VERIFIER = new NoopHostnameVerifier();
    private static final SSLContext SSL_CONTEXT;
    private static String isSecureMode = "true";
    private static String sslEnabled = "true";
    private static String esServerHost;
    private static int connectTimeout;
    private static int socketTimeout;
    private static int connectionRequestTimeout;
    private static int maxConnTotal;
    private static int maxConnPerRoute;
    private static String principal;
    private static final String SERVER_REALM_PREFIX = "elasticsearch/hadoop.";
    private static final String COMMA = ",";
    private static HttpHost[] hostArray;
    private static String customJaasPath;
    public static boolean snifferEnable = false;
    private String configPath;
    private String configName = "";
    private Properties properties = new Properties();

    public static String userKeytabFile = "";
    public static String krb5File = "";

    public Properties getProperties() {
        return this.properties;
    }

    /**
     *
     * @param configPath
     * @param configName
     * @param principalIn
     * @param userKeytabFileIn
     * @param krb5FileIN
     * @param customJaasPathIN
     */
    public ElasticsearchProperties(String configPath, String configName,
                                   String principalIn,String userKeytabFileIn, String krb5FileIN,
                                   String customJaasPathIN) {
        this.configPath = configPath;
        this.configName = configName;

        principal = principalIn;
        userKeytabFile = userKeytabFileIn;
        customJaasPath = customJaasPathIN;
        krb5File = krb5FileIN;


        if (!this.getConfig()) {
            LOG.error("Get config failed.");
        }


        LOG.info("principal={}",principal);
        LOG.info("customJaasPathIN={}",customJaasPath);

    }

    public ElasticsearchProperties(String configPath) {
        this.configPath = configPath;
        if (!this.getConfig()) {
            LOG.error("Get config failed.");
        }

    }

    public ElasticsearchProperties() {
        this.configPath = System.getProperty("user.dir") + File.separator + "conf" + File.separator;
        if (!this.getConfig()) {
            LOG.error("Get config failed.");
        }

    }

    private String getSchemeHeader() {
        String scheme = "https";
        if (!Boolean.parseBoolean(isSecureMode) || !Boolean.parseBoolean(sslEnabled)) {
            scheme = "http";
        }

        return scheme;
    }

    private boolean getConfig() {
        String path;
        if (null != this.configName && !"".equals(this.configName)) {
            path = this.configPath + this.configName;
        } else {
            path = this.configPath + "esParams.properties";
        }

        try {
            this.properties.load(new FileInputStream(new File(path)));
        } catch (IOException var3) {
            LOG.error("Failed to load properties file : " + path);
            return false;
        }

        try {
            esServerHost = this.properties.getProperty("esServerHost");
            connectTimeout = Integer.parseInt(this.properties.getProperty("connectTimeout"));
            socketTimeout = Integer.parseInt(this.properties.getProperty("socketTimeout"));
            connectionRequestTimeout = Integer.parseInt(this.properties.getProperty("connectionRequestTimeout"));
            maxConnPerRoute = Integer.parseInt(this.properties.getProperty("maxConnPerRoute"));
            maxConnTotal = Integer.parseInt(this.properties.getProperty("maxConnTotal"));
            isSecureMode = this.properties.getProperty("isSecureMode");
            //customJaasPath = ElasticsearchProperties.customJaasPath;
            sslEnabled = this.properties.getProperty("sslEnabled");
            if (sslEnabled == null || !Boolean.parseBoolean(isSecureMode)) {
                sslEnabled = isSecureMode;
            }

            if (Boolean.parseBoolean(isSecureMode)) {
                principal = this.principalHandler(principal, esServerHost);
                if (principal == null && (customJaasPath == null || customJaasPath.isEmpty())) {
                    return false;
                }
            }

            snifferEnable = Boolean.parseBoolean(this.properties.getProperty("snifferEnable"));
            if (LOG.isInfoEnabled()) {
                LOG.info("esServerHost:" + esServerHost);
                LOG.info("connectTimeout:" + connectTimeout);
                LOG.info("socketTimeout:" + socketTimeout);
                LOG.info("connectionRequestTimeout:" + connectionRequestTimeout);
                LOG.info("maxConnPerRouteTotal:" + maxConnPerRoute);
                LOG.info("maxConnTotal:" + maxConnTotal);
                LOG.info("isSecureMode:" + isSecureMode);
                LOG.info("sslEnabled:" + sslEnabled);
                LOG.info("principal:" + principal);
            }

            return true;
        } catch (NumberFormatException e) {
            LOG.error("Failed to get parameters !", e);
            return false;
        }
    }

    private String principalHandler(String principal, String hostAndPort) {
        if (principal != null && !principal.isEmpty()) {
            if (principal.contains("@")) {
                return principal;
            } else {
                String scheme = this.getSchemeHeader() + "://";
                String realm = this.getRealm(hostAndPort, scheme);
                if (realm != null && !realm.isEmpty()) {
                    return principal + "@" + realm;
                } else {
                    LOG.warn("The server realm is null.");
                    return principal;
                }
            }
        } else {
            LOG.warn("The principal is null because the properties is not set principal.");
            return null;
        }
    }

    private HttpHost[] getHostArray() {
        String schema = this.getSchemeHeader();
        List<HttpHost> hosts = new ArrayList();
        String[] hostArray1 = esServerHost.split(",");

        for(String host : hostArray1) {
            hosts.add(HttpHost.create(schema + "://" + host));
        }

        return (HttpHost[])hosts.toArray(new HttpHost[0]);
    }

    private void setSecConfig() {
        try {
            LOG.info("Config path is " + this.configPath);
            //String userKeytabFile = this.isEmpty(this.configPath) ? null : this.configPath + "user.keytab";
            ESLoginUtil.setJaasFile(principal,
                    userKeytabFile, customJaasPath);
            //String krb5File = this.isEmpty(this.configPath) ? null : this.configPath + "krb5.conf";
            ESLoginUtil.setKrb5Config(krb5File);
            System.setProperty("elasticsearch.kerberos.jaas.appname", "EsClient");
            System.setProperty("es.security.indication", "true");
            LOG.debug(String.format(Locale.ENGLISH, "es.security.indication is %s.", System.getProperty("es.security.indication")));
        } catch (Exception e) {
            LOG.error("Failed to set security conf.", e);
        }

    }

    public RestClientBuilder getRestClientBuilder() {
        hostArray = this.getHostArray();
        String jaasConf = null;
        if (!Boolean.parseBoolean(isSecureMode)) {
            System.setProperty("es.security.indication", "false");
        } else {
            this.setSecConfig();
            jaasConf = ESLoginUtil.getEsJaasConfPath();
        }

        RestClientBuilder builder = RestClient.builder(hostArray);
        builder.setSslEnabled(Boolean.parseBoolean(sslEnabled));
        if (jaasConf != null && !jaasConf.isEmpty() && Boolean.parseBoolean(isSecureMode)) {
            RestClientBuilder.setEsJaasConfFile(jaasConf);
        }

        Header[] defaultHeaders = new Header[]{new BasicHeader("Accept", "application/json"), new BasicHeader("Content-type", "application/json")};
        builder.setRequestConfigCallback((requestConfigBuilder) -> requestConfigBuilder.setConnectTimeout(connectTimeout).setSocketTimeout(socketTimeout).setConnectionRequestTimeout(connectionRequestTimeout));
        builder.setHttpClientConfigCallback((httpClientBuilder) -> httpClientBuilder.setMaxConnPerRoute(maxConnPerRoute).setMaxConnTotal(maxConnTotal));
        builder.setDefaultHeaders(defaultHeaders);
        return builder;
    }

    public RestClient getRestClient() {
        if (this.ifConfigWasWrong()) {
            return null;
        } else {
            RestClientBuilder restClientBuilder = this.getRestClientBuilder();
            RestClient restClient = restClientBuilder.build();
            this.setNodes(restClient);
            LOG.info("The Low Level Rest Client has been created.");
            return restClient;
        }
    }

    private boolean ifConfigWasWrong() {
        if (this.isEmpty(this.configPath)) {
            LOG.info("Config path is not allowed to be empty.");
            return true;
        } else {
            return false;
        }
    }

    private void setNodes(RestClient restClient) {
        List<Node> nodes = new ArrayList();

        for(HttpHost httpHost : hostArray) {
            nodes.add(new Node(httpHost));
        }

        restClient.setNodes(nodes);
    }

    private String getRealm(String hostAndPorts, String scheme) {
        if (this.isEmpty(hostAndPorts)) {
            LOG.error("Host and port is empty.");
            return null;
        } else if (this.isEmpty(scheme)) {
            LOG.error("Scheme is empty.");
            return null;
        } else {
            String[] hostAndPortArray = hostAndPorts.split(",");

            for(String hostAndPort : hostAndPortArray) {
                String serverRealm = this.getServerRealm(hostAndPort, scheme);
                if (serverRealm != null && !serverRealm.isEmpty()) {
                    if (serverRealm.toLowerCase(Locale.ENGLISH).startsWith("elasticsearch/hadoop.")) {
                        return serverRealm.substring(serverRealm.indexOf("@") + 1);
                    }

                    return serverRealm;
                }
            }

            LOG.error("No available services, server realm is null.");
            return null;
        }
    }

    private String getServerRealm(String hostAndPort, String scheme) {
        String serverRealm = null;
        InputStream is = null;
        ByteArrayOutputStream os = null;

        try {
            HttpClientBuilder builder = HttpClientBuilder.create();
            builder.setSSLHostnameVerifier(HOSTNAME_VERIFIER);
            builder.setSSLContext(SSL_CONTEXT);
            HttpClient client = builder.build();
            HttpGet httpGet = new HttpGet(scheme + hostAndPort + "/elasticsearch/serverrealm");
            HttpResponse response = client.execute(httpGet);
            int httpStatus = response.getStatusLine().getStatusCode();
            if (200 == httpStatus) {
                is = response.getEntity().getContent();
                os = new ByteArrayOutputStream();
                byte[] buffer = new byte[64];
                int size = -1;

                while(-1 != (size = is.read(buffer))) {
                    os.write(buffer, 0, size);
                }

                serverRealm = os.toString(StandardCharsets.UTF_8.displayName());
                LOG.info("Success to get the service realm " + serverRealm + ".");
            } else {
                LOG.error("Cannot get server realm at " + hostAndPort + ".");
            }
        } catch (Exception e) {
            LOG.error("Get server realm failed.", e);
        } finally {
            if (os != null) {
                try {
                    os.close();
                } catch (IOException e) {
                    LOG.error("Close http response byte array output stream error.", e);
                }
            }

            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    LOG.error("Close http response input stream failed.", e);
                }
            }

        }

        return serverRealm;
    }

    private boolean isEmpty(String value) {
        return value == null || value.isEmpty();
    }

    static {
        TrustStrategy trustStrategy = new TrustStrategy() {
            public boolean isTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
                return true;
            }
        };

        try {
            SSL_CONTEXT = (new SSLContextBuilder()).loadTrustMaterial((KeyStore)null, trustStrategy).build();
        } catch (NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) {
            LOG.error("Init ssl context failed.", e);
            throw new RuntimeException(e);
        }
    }

    /*private static Properties properties = new Properties();

    private static final String CONFIGURATION_FILE_NAME = "esParams.properties";

    *//**
     * 配置文件默认配置路径
     *//*
    private static final String DEFAULT_CONFIG_PATH = "conf";

    private static final String JAR_EXT = ".jar";

    private static final String KEY_TAB_PATH = "keytabPath";

    private static final String KRB_5_PATH = "krb5Path";

    private static final String CUSTOM_JAAS_PATH = "customJaasPath";

    private static String configPath;

    *//**
     * 加载配置文件
     *
     * @param configPath 参数列表
     * @return 配置对下
     * @throws IOException 异常
     *//*
    public static Configuration loadProperties(String configPath, String principal,
                                               String krb5Path, String keytabPath, String customJaasPath) throws IOException {
        //initConfigPath(args);
        ElasticsearchProperties.configPath = configPath;
        initProperties();
        Configuration configuration = new Configuration();
        configuration.setClusterName(loadClusterName());
        configuration.setTransportAddress(loadTransportAddress());
        configuration.setSecureMode(loadIsSecureMode());
        if (configuration.isSecureMode()) {
            configuration.setPrincipal(principal); //configuration.setPrincipal(loadPrincipal());
            configuration.setKeyTabPath(keytabPath); //configuration.setKeyTabPath(loadPath(KEY_TAB_PATH));
            configuration.setKrb5Path(krb5Path);
            configuration.setSslEnabled(loadSslEnabled());
            configuration.setCustomJaasPath(customJaasPath); //configuration.setCustomJaasPath(loadCustomJaasPath());
        }
        configuration.setSniff(loadIsSniff());
        LOG.info("configuration: {}", configuration);
        return configuration;
    }

    *//*public static void main(String[] args) {
        try {
            initConfigPath(args);
            System.out.println(configPath);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }*//*

    private static void initProperties() {
        try {
            properties.load(new FileInputStream(new File(configPath)));
        } catch (IOException e) {
            LOG.error("Failed to load properties file, config file path is {}.", configPath, e);
            throw new IllegalArgumentException();
        }
    }

    private static void initConfigPath(String[] args) throws IOException {
        // 配置文件路径参数位置
        int configPathArgumentIndex = 0;
        if (args == null || args.length < 1 || args[configPathArgumentIndex] == null
                || args[configPathArgumentIndex].isEmpty()) {
            configPath = getCurrentJarPath() + CONFIGURATION_FILE_NAME;
        } else {
            configPath = args[configPathArgumentIndex];
            File configFile = new File(configPath);
            if (configFile.exists() && configFile.isDirectory()) {
                configPath = configPath + File.separator + CONFIGURATION_FILE_NAME;
            }
        }
    }

    *//**
     * 读取clusterName参数
     *
     * @return 参数值
     *//*
    public static String loadClusterName() {
        String clusterName = properties.getProperty("clusterName");
        if (clusterName == null || clusterName.isEmpty()) {
            LOG.error("clusterName is empty, please configure it in config file : {}.", configPath);
            throw new IllegalArgumentException();
        }
        return clusterName;
    }

    private static Set<TransportAddress> loadTransportAddress() {
        String serverHosts = properties.getProperty("esServerHosts");
        if (serverHosts == null || serverHosts.isEmpty()) {
            LOG.error("Please configure esServerHosts in conf/{}.", CONFIGURATION_FILE_NAME);
            LOG.error("The format of esServerHosts is ip1:port1,ip2:port2,ipn:portn");
            throw new IllegalArgumentException();
        }
        String[] hosts = serverHosts.split(",");
        Set<TransportAddress> transportAddresses = new HashSet<>(hosts.length);
        for (String host : hosts) {
            String esClientIp = "";
            int port = -1;
            final int portIdx = host.lastIndexOf(":");
            if (portIdx > 0) {
                try {
                    port = Integer.parseInt(host.substring(portIdx + 1));
                } catch (final NumberFormatException ex) {
                    throw new IllegalArgumentException("Invalid HTTP host: " + host);
                }
                esClientIp = host.substring(0, portIdx);
            }
            if (-1 == port) {
                LOG.error("The configuration  esClientIPPort is empty, please configure it in config file : {}.",
                        configPath);
                throw new IllegalArgumentException();
            }
            try {
                if (port % 2 == 0) {
                    LOG.warn("The configuration esClientIPPort may be wrong, please check it in config file : {}.",
                            configPath);
                }
            } catch (NumberFormatException e) {
                LOG.warn("The configuration esClientIPPort may be wrong, please check it in config file : {}.",
                        configPath);
                throw new IllegalArgumentException();
            }
            try {
                transportAddresses.add(new TransportAddress(InetAddress.getByName(esClientIp), port));
            } catch (NumberFormatException | UnknownHostException e) {
                LOG.error("Init esServerHosts occur error : {}.", e.getMessage());
                throw new IllegalArgumentException();
            }
        }
        return transportAddresses;
    }

    private static String loadPath(String path) throws IOException {
        String loadedPath = properties.getProperty(path);
        if (loadedPath == null || loadedPath.isEmpty()) {
            loadedPath = getCurrentJarPath();
            if (!loadedPath.endsWith(File.separator)) {
                loadedPath += File.separator;
            }
            LOG.warn("Config path is empty, using the default path : {} .", loadedPath);
        }
        return loadedPath;
    }

    private static String loadCustomJaasPath() {
        String jaasPath = properties.getProperty(CUSTOM_JAAS_PATH);
        if (jaasPath == null || jaasPath.isEmpty()) {
            return "";
        }

        return jaasPath;
    }

    private static boolean loadIsSecureMode() {
        return !Boolean.FALSE.toString().equals(properties.getProperty("isSecureMode"));
    }

    private static boolean loadSslEnabled() {
        return !Boolean.FALSE.toString().equals(properties.getProperty("sslEnabled"));
    }

    private static boolean loadIsSniff() {
        return !Boolean.FALSE.toString().equals(properties.getProperty("isSniff"));
    }

    private static String loadPrincipal() {
        String principal = properties.getProperty("principal");
        if (principal == null || principal.isEmpty()) {
            LOG.error("Please configure principal config file : {} .", configPath);
            throw new IllegalArgumentException();
        }
        return principal;
    }


    private static String getCurrentJarPath() throws IOException {
        StringBuilder currentJarPath = new StringBuilder();
        URL url = ElasticsearchProperties.class.getProtectionDomain().getCodeSource().getLocation();
        try {
            String filePath = URLDecoder.decode(url.getPath(), "UTF-8");
            if (filePath.endsWith(JAR_EXT)) {
                // 获取jar包所在目录
                filePath = filePath.substring(0, filePath.lastIndexOf(File.separator) + 1);
                File file = new File(filePath);
                currentJarPath.append(file.getCanonicalPath());
                if (!currentJarPath.toString().endsWith(File.separator)) {
                    currentJarPath.append(File.separator);
                }
                currentJarPath.append("..").append(File.separator).append(DEFAULT_CONFIG_PATH).append(File.separator);
            } else {
                currentJarPath.append(System.getProperty("user.dir"))
                        .append(File.separator)
                        .append(DEFAULT_CONFIG_PATH)
                        .append(File.separator);
            }
        } catch (IOException ex) {
            LOG.error("Get current jar path error.", ex);
            throw ex;
        }
        return currentJarPath.toString();
    }*/
}
